/******************************************************************************* * Signavio Core Components * Copyright (C) 2012 Signavio GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package de.hpi.bpmn2_0.factory; import java.util.ArrayList; import java.util.List; import org.oryxeditor.server.diagram.Bounds; import org.oryxeditor.server.diagram.Point; import org.oryxeditor.server.diagram.generic.GenericEdge; import org.oryxeditor.server.diagram.generic.GenericShape; import de.hpi.bpmn2_0.exceptions.BpmnConverterException; import de.hpi.bpmn2_0.model.BaseElement; import de.hpi.bpmn2_0.model.bpmndi.BPMNEdge; import de.hpi.bpmn2_0.transformation.Diagram2BpmnConverter; import de.hpi.bpmn2_0.util.DiagramHelper; /** * Abstract factory that contains basic methods to create edges. * * @author Philipp Giese * @author Sven Wagner-Boysen * */ public abstract class AbstractEdgesFactory extends AbstractBpmnFactory { /* * (non-Javadoc) * * @see * de.hpi.bpmn2_0.factory.common.AbstractBpmnFactory#createBpmnElement(org * .oryxeditor.server.diagram.Shape, de.hpi.bpmn2_0.factory.BPMNElement) */ // @Override public BPMNElement createBpmnElement(GenericShape shape, BPMNElement parent) throws BpmnConverterException { BPMNEdge diaElement = this.createDiagramElement(shape); BaseElement processElement = this.createProcessElement(shape); diaElement.setBpmnElement(processElement); super.setLabelPositionInfo(shape, processElement); BPMNElement bpmnElement = new BPMNElement(diaElement, processElement, shape.getResourceId()); // handle external extension elements like from Activiti try { super.reinsertExternalExtensionElements(shape, bpmnElement); } catch (Exception e) { } return bpmnElement; } /* * (non-Javadoc) * * @see * de.hpi.bpmn2_0.factory.common.AbstractBpmnFactory#createDiagramElement * (org.oryxeditor.server.diagram.Shape) */ // @Override protected BPMNEdge createDiagramElement(GenericShape shape) { BPMNEdge bpmnEdge = new BPMNEdge(); super.setVisualAttributes(bpmnEdge, shape); if (shape instanceof GenericEdge) bpmnEdge.getWaypoint().addAll(this.generateBendpoints((GenericEdge)shape)); return bpmnEdge; } /** * Creates the bend points of an edge, starting with the second to second * last docker of the edge's shape. * * @param shape * @return */ private List<de.hpi.bpmn2_0.model.bpmndi.dc.Point> generateBendpoints( GenericEdge<?,?> shape) { List<de.hpi.bpmn2_0.model.bpmndi.dc.Point> wayPoints = new ArrayList<de.hpi.bpmn2_0.model.bpmndi.dc.Point>(); for (int i = 0; i < shape.getDockersReadOnly().size(); i++) { Point wayPoint = shape.getDockersReadOnly().get(i); de.hpi.bpmn2_0.model.bpmndi.dc.Point bpmnPoint = new de.hpi.bpmn2_0.model.bpmndi.dc.Point( wayPoint); /* Convert first docker from relative to source shape to absolute */ if (i == 0) { /* Retrieve source element */ GenericShape sourceShape = shape.getSource(); if (sourceShape != null) { Bounds diagramBounds = sourceShape.getAbsoluteBounds(); bpmnPoint.setX(bpmnPoint.getX() + diagramBounds.getUpperLeft().getX()); bpmnPoint.setY(bpmnPoint.getY() + diagramBounds.getUpperLeft().getY()); } } /* Convert last docker from relative to absolute */ else if (i == shape.getDockersReadOnly().size() - 1) { GenericShape targetShape = shape.getTarget(); if (targetShape != null) { Bounds diagramBounds = targetShape.getAbsoluteBounds(); bpmnPoint.setX(bpmnPoint.getX() + diagramBounds.getUpperLeft().getX()); bpmnPoint.setY(bpmnPoint.getY() + diagramBounds.getUpperLeft().getY()); } } /* All other dockers are already coded in absolute coordinates */ wayPoints.add(bpmnPoint); } /* Calculate intersection point each with source and target shape */ GenericShape sourceShape = shape.getSource(); if (sourceShape != null) { de.hpi.bpmn2_0.model.bpmndi.dc.Point intersectionPoint = getIntersectionPoint( sourceShape.getAbsoluteBounds(), wayPoints.get(0), wayPoints.get(1)); wayPoints.remove(0); wayPoints.add(0, intersectionPoint); } GenericShape targetShape = shape.getTarget(); if (targetShape != null) { de.hpi.bpmn2_0.model.bpmndi.dc.Point intersectionPoint = getIntersectionPoint( targetShape.getAbsoluteBounds(), wayPoints.get(shape.getDockersReadOnly().size() - 1), wayPoints.get(shape.getDockersReadOnly().size() - 2)); wayPoints.remove(shape.getDockersReadOnly().size() - 1); wayPoints.add(shape.getDockersReadOnly().size() - 1, intersectionPoint); } return wayPoints; } private de.hpi.bpmn2_0.model.bpmndi.dc.Point getIntersectionPoint( Bounds bounds, de.hpi.bpmn2_0.model.bpmndi.dc.Point a, de.hpi.bpmn2_0.model.bpmndi.dc.Point b) { // Calculates the intersection between a and b and the bounds Algorithm alg = new Algorithm(); alg.cohenSutherland(a.getX().intValue(), a.getY().intValue(), b.getX() .intValue(), b.getY().intValue(), bounds.getUpperLeft().getX() .intValue(), bounds.getUpperLeft().getY().intValue(), bounds .getLowerRight().getX().intValue(), bounds.getLowerRight() .getY().intValue()); if (alg.getx2() != 0 && alg.gety2() != 0) return new de.hpi.bpmn2_0.model.bpmndi.dc.Point(alg.getx2(), alg .gety2()); else return a; } private class Algorithm { private int RIGHT = 2; private int TOP = 8; private int BOTTOM = 4; private int LEFT = 1; private int x1, x2, y1, y2; public int computeOutCode(int x, int y, int xmin, int ymin, int xmax, int ymax) { int code = 0; if (y > ymax) code |= TOP; else if (y < ymin) code |= BOTTOM; if (x > xmax) code |= RIGHT; else if (x < xmin) code |= LEFT; return code; } public void cohenSutherland(int x1, int y1, int x2, int y2, int xmin, int ymin, int xmax, int ymax) { // Outcodes for P0, P1, and whatever point lies outside the clip // rectangle int outcode0, outcode1, outcodeOut, hhh = 0; boolean accept = false, done = false; // compute outcodes outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax); outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax); do { if ((outcode0 | outcode1) == 0) { accept = true; done = true; } else if ((outcode0 & outcode1) > 0) { done = true; } else { // failed both tests, so calculate the line segment to clip // from an outside point to an intersection with clip edge int x = 0, y = 0; // At least one endpoint is outside the clip rectangle; pick // it. outcodeOut = outcode0 != 0 ? outcode0 : outcode1; // Now find the intersection point; // use formulas y = y0 + slope * (x - x0), x = x0 + // (1/slope)* (y - y0) if ((outcodeOut & TOP) > 0) { x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1); y = ymax; } else if ((outcodeOut & BOTTOM) > 0) { x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1); y = ymin; } else if ((outcodeOut & RIGHT) > 0) { y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1); x = xmax; } else if ((outcodeOut & LEFT) > 0) { y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1); x = xmin; } // Now we move outside point to intersection point to clip // and get ready for next pass. if (outcodeOut == outcode0) { x1 = x; y1 = y; outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax); } else { x2 = x; y2 = y; outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax); } } hhh++; } while (done != true && hhh < 5000); if (accept) { set(x1, y1, x2, y2); } } public void set(int x1, int y1, int x2, int y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } public int getx1() { return x1; } public int getx2() { return x2; } public int gety1() { return y1; } public int gety2() { return y2; } } }